//NRU Way-Associative replace arbiter & tag manager
//对于CPU访存提供写穿透策略/写回策略接口，配套了内存同步控制线。
`include "PRV564Config.v"
`include "PRV564Define.v"
module tagman_wa
#(
    parameter WAY_DEPTH=32 //How many entries does 1 way have?
    ,WAY_NUM=4//MUST > 1
    ,INDEX_WID = ((WAY_NUM > 1) ? $clog2(WAY_NUM) : 1)
    ,TAG_WID=14
    ,WBACK_ENABLE=1'b0 //this actually heavily depends on optimization
    ,WAYADDR_WID = $clog2(WAY_DEPTH)
    
)
(
    input clk,
    input rst,
    //Core interface
    input entry_read,   
    input entry_wthru,
    input entry_wback,
    input [TAG_WID-1:0]address_tag,
    input [WAYADDR_WID-1:0]address_index,	
    input dfence_en,//start mem sync
    input valid_clear, //flush
    //BIU interface
    //input [TAG_WID-1:0]refill_tag,
    input fill_finish, //cachemem write, confirm refill valid
    input writeback_ok,//data block write back complete, from cache BU
    output line_miss,//refill request
    output replace_dirty, //page dirty, need to write back first
    output dirty_comp,
    output [TAG_WID-1:0]way_dirty_addr,//When D Fence, the address of valid dirty line to write back
    output [WAYADDR_WID-1:0]dirty_index,//When D Fence, we use the index provided by Dirty bank selector
    output [INDEX_WID-1:0]way_replace_addr, //for replace select& dirty flush select
    output [INDEX_WID-1:0]way_access_addr//addr for access 
);
genvar i;integer j,k,l,m,n;
reg [TAG_WID-1:0]TagMemArray[WAY_NUM*WAY_DEPTH-1:0];
reg [WAY_NUM-1:0]TagValidRegArray[WAY_DEPTH-1:0];
wire [TAG_WID-1:0]WayTagRead[WAY_NUM-1:0];
wire [WAY_NUM-1:0]WayHit;
wire [WAY_NUM-1:0]WayValid;
wire ValidBit;
wire CacheAccess;
assign CacheAccess = entry_read | entry_wthru | entry_wback;
assign line_miss = (WayHit == 0 | (!ValidBit)) & CacheAccess;
assign WayValid = TagValidRegArray[address_index];
assign ValidBit = |(WayValid & WayHit);
//Way Hit logic
generate 
    for(i=0;i<WAY_NUM;i=i+1)
    begin  : WAY_CMP_GEN
        assign WayTagRead[i]=TagMemArray[{i[INDEX_WID-1:0],address_index}];
        assign WayHit[i]=(WayTagRead[i]==address_tag); //TODO add valid
    end
endgenerate
//Tag Valid logic
always@(posedge clk or posedge rst)
begin
    if(rst)
        for(n=0;n<WAY_DEPTH;n=n+1)
        TagValidRegArray[n]=0;
    else if (valid_clear)
    for(n=0;n<WAY_DEPTH;n=n+1)
        TagValidRegArray[n]=0;
    else if(fill_finish)
        TagValidRegArray[address_index]= WayValid | (1 << way_replace_addr);
    else
        TagValidRegArray[address_index]=TagValidRegArray[address_index];
end
//Way Hit encode logic
reg [INDEX_WID-1:0]WayAccessEncArr[WAY_NUM-1:0];
assign way_access_addr=WayAccessEncArr[WAY_NUM-1];
always @(*)
begin
    WayAccessEncArr[0]=0;
    for ( j=1;j<WAY_NUM;j=j+1 ) 
    begin
        WayAccessEncArr[j]=sel_encode_cell(WayAccessEncArr[j-1],j,WayHit[j]);
    end
end
//Tag Array Block
always@(posedge clk or posedge rst)
begin
    if(rst)
        for(m=0;m<WAY_NUM*WAY_DEPTH;m=m+1)
            TagMemArray[m]=0;
    else if(fill_finish)
        TagMemArray[{way_replace_addr,address_index}]=address_tag;
    else
        TagMemArray[{way_replace_addr,address_index}]=TagMemArray[{way_replace_addr,address_index}];
end
//NRU History Regs
reg [WAY_NUM-1:0]TagHistoryArray[WAY_DEPTH-1:0];
wire [WAY_NUM-1:0]CurrWayHistory;
assign CurrWayHistory=TagHistoryArray[address_index];
always@(posedge clk or posedge rst)
begin
    if(rst)
        for(k=0;k<WAY_DEPTH;k=k+1)
            TagHistoryArray[k]=0;
    else if(CacheAccess)
    begin
        if((~CurrWayHistory)==0)
            TagHistoryArray[address_index]=0;
        else 
            TagHistoryArray[address_index]=CurrWayHistory | WayHit;
    end
    else
        TagHistoryArray[address_index]=TagHistoryArray[address_index];
end
//Replacement Select
reg [INDEX_WID-1:0]WayReplaceEncArr[WAY_NUM-1:0];
assign way_replace_addr=WayReplaceEncArr[WAY_NUM-1];
always @(*)//替换/写回选择优先编码器
begin
    WayReplaceEncArr[0]=0;
    for ( l=1;l<WAY_NUM;l=l+1 ) //从LSB向MSB扫描，高位cache单元优先换出
    begin
        WayReplaceEncArr[l]=sel_encode_cell(WayReplaceEncArr[l-1],l,!CurrWayHistory[l]);//为0才换出
    end
end
//写回管理
generate 
if(WBACK_ENABLE)
begin : LINE_DIRTY_MANAGEMENT 
    integer o,p;
    reg [WAY_NUM-1:0]TagDirtyRegArray[WAY_DEPTH-1:0];
    wire [WAY_NUM-1:0]CurrLineDirty;
    wire [WAYADDR_WID-1:0]DirtyTagReadAddr;
    reg [WAY_NUM-1:0]DirtyInfoForCmp[WAY_DEPTH-1:0];
    assign CurrLineDirty=DirtyInfoForCmp[address_index];
    assign replace_dirty = CurrLineDirty[way_replace_addr]; 
    assign DirtyTagReadAddr=(dfence_en)?dirty_index:address_index;
    assign dirty_comp = CurrLineDirty[way_access_addr];
    assign way_dirty_addr=TagMemArray[DirtyTagReadAddr];
    //generate 
    always@(*)
        for(o=0;o<WAY_DEPTH;o=o+1)
            DirtyInfoForCmp[o]=TagDirtyRegArray[o] & TagValidRegArray[o];
    //endgenerate
    always@(posedge clk or posedge rst)
    begin
        if(rst)//TODO FOR NON-VERILATOR test, non-blocking is needed
        for(p=0;p<WAY_DEPTH;p=p+1)
            TagDirtyRegArray[p]=0;
        else if(entry_wback & (!line_miss))
            TagDirtyRegArray[address_index]=TagDirtyRegArray[address_index] | (1'b1 << way_access_addr);
        else if(writeback_ok)
            TagDirtyRegArray[address_index]=TagDirtyRegArray[address_index] & (~(1'b1 << way_access_addr));
        else
            TagDirtyRegArray[address_index]=TagDirtyRegArray[address_index];
    end
    //Dirty way encode (for D Fence)
    reg [WAYADDR_WID-1:0]IndexDirtyEncode[WAY_DEPTH-1:0];
    assign dirty_index=IndexDirtyEncode[WAY_DEPTH-1];
    always @(*) 
    begin
        IndexDirtyEncode[0]=0;
        for(j=1;j<WAY_DEPTH;j=j+1)
            IndexDirtyEncode[j]=set_encode_cell(IndexDirtyEncode[j-1],j,(DirtyInfoForCmp[j] != 0));
    end
    
    //可扩展优先编码器：如果该位为1,返回该位所在编号,如果该位为0，传递上一级输入编号
    function [WAYADDR_WID-1:0]set_encode_cell;
        input [WAYADDR_WID-1:0]prev_set_num;
        input [WAYADDR_WID-1:0]cur_set_num;
        input cur_set_bit;
        begin
            set_encode_cell=(cur_set_bit)?cur_set_num:prev_set_num;
        end
    endfunction
end
else 
begin: NO_WRITEBACK
    assign replace_dirty=1'b0;
    assign way_dirty_addr= 'bx;
end
endgenerate

function [WAY_NUM-1:0]sel_decode_sel;
    input [INDEX_WID-1:0]sel_encode_in;
    integer l;
    for(l=0;l<WAY_NUM;l=l+1) 
    begin //: SEL_BITGEN
        sel_decode_sel[l]=(sel_encode_in==l);
    end
endfunction

//可扩展优先编码器：如果该位为1,返回该位所在编号,如果该位为0，传递上一级输入编号
function [INDEX_WID-1:0]sel_encode_cell;
    input [INDEX_WID-1:0]prev_entry_num;
    input [INDEX_WID-1:0]cur_entry_num;
    input cur_entry_bit;
    begin
   		sel_encode_cell=(cur_entry_bit)?cur_entry_num:prev_entry_num;
   	end
endfunction
endmodule


